package com.example.teach.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.teach.DTO.ClassCoreCacheDTO;
import com.example.teach.entity.*;
import com.example.teach.mapper.*;
import com.example.teach.service.StudentService;

import com.example.teach.service.UserService;
import com.example.teach.vo.ClassVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@CacheConfig(cacheNames = "student:page")
public class StudentServiceImpl extends ServiceImpl<StudentMapper, ClassVO> implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ClassMapper classMapper;

    @Autowired
    private MajorMapper majorMapper;

    @Autowired
    private CourseMapper courseMapper;

    @Autowired
    private ScheduleMapper scheduleMapper;

    @Autowired
    private UserClassMapper userClassMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    @Cacheable(key = "'getPage_' + " +
            "(#teacherName != null ? #teacherName : 'null') + '_' + " +
            "(#className != null ? #className : 'null') + '_' + " +
            "(#majorId != null ? #majorId : 'null') + '_' + " +
            "(#courseId != null ? #courseId : 'null') + '_' + " +
            "(#remaining != null ? #remaining : 'null') + '_' + " +
            "#pageNum + '_' + #pageSize")
    public Result getPage(String teacherName, String className,
                          Integer majorId, Integer courseId, Integer remaining,
                          Integer pageNum, Integer pageSize) {

        Page<Clazz> clazzPage = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<Clazz> queryWrapper = new LambdaQueryWrapper<>();
        if (teacherName != null && !teacherName.isEmpty()) {
            List<User> userList = userMapper.selectlike(teacherName);
            if (!userList.isEmpty()) {
                List<Integer> teacherIds = userList.stream()
                        .map(User::getUserId)
                        .collect(Collectors.toList());
                queryWrapper.in(Clazz::getTeacherId, teacherIds);
            } else {
                return Result.success(new Page<>());
            }
        }
        if (className != null && !className.isEmpty()) {
            queryWrapper.like(Clazz::getClassName, className);
        }
        if (majorId != null) {
            queryWrapper.eq(Clazz::getMajorId, majorId);
        }
        if (courseId != null) {
            queryWrapper.eq(Clazz::getCourseId, courseId);
        }
        if (remaining != null) {
            queryWrapper.eq(Clazz::getRemaining, remaining);
        }
        queryWrapper.ne(Clazz::getState, 1);

        Page<Clazz> resultPage = classMapper.selectPage(clazzPage, queryWrapper);

        List<ClassVO> classVOList = resultPage.getRecords().stream().map(clazz -> {
            ClassVO classVO = new ClassVO();
            classVO.setId(clazz.getId());
            classVO.setTeacherId(clazz.getTeacherId());
            classVO.setClassName(clazz.getClassName());
            classVO.setMajorId(clazz.getMajorId());
            classVO.setCourseId(clazz.getCourseId());
            classVO.setNumber(clazz.getNumber());
            classVO.setExisting(clazz.getExisting());
            classVO.setRemaining(clazz.getRemaining());
            classVO.setVersion(clazz.getVersion());

            User teacher = userMapper.selectByteacherId(clazz.getTeacherId());
            classVO.setTeacherName(teacher != null ? teacher.getUserName() : "玉渊潭天");

            Major major = majorMapper.selectById(clazz.getMajorId());
            classVO.setMajorName(major != null ? major.getMajorName() : "未知专业");

            Course course = courseMapper.selectById(clazz.getCourseId());
            classVO.setCourseName(course != null ? course.getCourseName() : "未知课程");

            Schedule schedule = scheduleMapper.selectOne(new QueryWrapper<Schedule>().eq("class_id", clazz.getId()));
            if (schedule != null) {
                classVO.setStartWeek(schedule.getStartWeek());
                classVO.setEndWeek(schedule.getEndWeek());
                classVO.setDay(schedule.getDay());
                classVO.setStartTime(schedule.getStartTime());
                classVO.setEndTime(schedule.getEndTime());
                classVO.setClassroom(schedule.getClassroom());
            } else {
                classVO.setClassroom("无上课地点");
            }

            ClassCoreCacheDTO classCoreCache = new ClassCoreCacheDTO();
            classCoreCache.setClassId(clazz.getId());
            classCoreCache.setExisting(clazz.getExisting());
            classCoreCache.setNumber(clazz.getNumber());
            classCoreCache.setRemaining(clazz.getRemaining());
            classCoreCache.setState(clazz.getState());
            classCoreCache.setVersion(clazz.getVersion());

            String coreCacheKey = "student:class:core:" + clazz.getId();
            redisTemplate.opsForValue().set(coreCacheKey,classCoreCache,10, TimeUnit.MINUTES);

            return classVO;
        }).collect(Collectors.toList());

        Page<ClassVO> voPage = new Page<>();
        voPage.setRecords(classVOList);
        voPage.setTotal(resultPage.getTotal());
        voPage.setSize(resultPage.getSize());
        voPage.setCurrent(resultPage.getCurrent());
        voPage.setPages(resultPage.getPages());

        return Result.success(voPage);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result add(UserClass userClass) {
        if(userClass == null || userClass.getUserId() == null || userClass.getClassId() == null){
            return Result.error("参数不能为空");
        }
        Integer userId = userClass.getUserId();
        Integer classId = userClass.getClassId();
        String coreCacheKey = "student:class:core:" + classId;
        try{
            LambdaQueryWrapper<UserClass> uc = new LambdaQueryWrapper<>();
            uc.eq(UserClass::getUserId,userId)
                    .eq(UserClass::getClassId,classId);
            UserClass existingUserClass = userClassMapper.selectOne(uc);
            if(existingUserClass != null){
                return Result.error("已加入该班级");
            }
            ClassCoreCacheDTO coreCache = (ClassCoreCacheDTO) redisTemplate.opsForValue().get(coreCacheKey);
            if(coreCache != null){
                if(coreCache.getState() == 1){
                    return Result.error("该班级已解散");
                }
                if(coreCache.getExisting() >= coreCache.getNumber()){
                    return Result.error("班级已满员");
                }
            }
            Clazz clazz = classMapper.selectById(classId);
            if(clazz == null){
                redisTemplate.opsForValue().set(coreCacheKey, new ClassCoreCacheDTO(), 1, TimeUnit.MINUTES);
                return Result.error("班级不存在");
            }
            if(clazz.getState() == 1){
                return Result.error("班级解散了");
            }
            if(clazz.getExisting() >= clazz.getNumber()){
                return Result.error("班级没有空余名额了");
            }
            int newExisting = clazz.getExisting() + 1;
            int newRemaining = (newExisting >= clazz.getNumber()) ? 1 : 0;
            int updateCount = classMapper.updateClassNum(
                    clazz.getId(),
                    newExisting,
                    newRemaining,
                    clazz.getVersion()
            );
            if(updateCount == 0){
                return Result.error("选课失败，请稍后重试");
            }

            userClassMapper.insert(userClass);

            ClassCoreCacheDTO newCoreCache = new ClassCoreCacheDTO();
            newCoreCache.setClassId(clazz.getId());
            newCoreCache.setExisting(newExisting);
            newCoreCache.setNumber(clazz.getNumber());
            newCoreCache.setRemaining(newRemaining);
            newCoreCache.setState(clazz.getState());
            newCoreCache.setVersion(clazz.getVersion() + 1);
            redisTemplate.opsForValue().set(coreCacheKey, newCoreCache, 10, TimeUnit.MINUTES);

            Set<String> pageCacheKeys = redisTemplate.keys("student:page::getPage_*");
            if(pageCacheKeys != null && !pageCacheKeys.isEmpty()){
                redisTemplate.delete(pageCacheKeys);
            }
            return Result.success("选课成功");
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result reduce(UserClass userClass) {
        if(userClass == null || userClass.getUserId() == null || userClass.getClassId() == null){
            return Result.error("参数不能为空");
        }
        Integer userId = userClass.getUserId();
        Integer classId = userClass.getClassId();
        String coreCacheKey = "student:class:core:" + classId;

        try{
            LambdaQueryWrapper<UserClass> uc = new LambdaQueryWrapper<>();
            uc.eq(UserClass::getUserId,userId)
                    .eq(UserClass::getClassId,classId);
            UserClass existingUserClass = userClassMapper.selectOne(uc);
            if(existingUserClass == null){
                return Result.error("没选该班级，不能退课");
            }
            Clazz clazz = classMapper.selectById(classId);
            if(clazz == null){
                redisTemplate.opsForValue().set(coreCacheKey,new ClassCoreCacheDTO(), 1, TimeUnit.MINUTES);
                return Result.error("班级不存在");
            }
            int newExisting = clazz.getExisting() - 1;
            int newRemaining = (newExisting >= clazz.getNumber()) ? 1 : 0;
            int updateCount = classMapper.updateClassNum(
                    clazz.getId(),
                    newExisting,
                    newRemaining,
                    clazz.getVersion()
            );
            if(updateCount == 0){
                return Result.error("退课失败，请稍后重试");
            }

            userClassMapper.delete(uc);

            ClassCoreCacheDTO newCoreCache = new ClassCoreCacheDTO();
            newCoreCache.setClassId(clazz.getId());
            newCoreCache.setExisting(newExisting);
            newCoreCache.setNumber(clazz.getNumber());
            newCoreCache.setRemaining(newRemaining);
            newCoreCache.setState(clazz.getState());
            newCoreCache.setVersion(clazz.getVersion() + 1);
            redisTemplate.opsForValue().set(coreCacheKey, newCoreCache, 10, TimeUnit.MINUTES);

            Set<String> pageCacheKeys = redisTemplate.keys("student:page::getPage_*");
            if(pageCacheKeys != null && !pageCacheKeys.isEmpty()){
                redisTemplate.delete(pageCacheKeys);
            }
            return Result.success("退课成功");

        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }

    @Override
    public Result selectClassSchedule(Integer userId) {

        return null;
    }




    /*
    废弃
     */
    /*@Override
    @Cacheable(key = "'getPage_' + " +
            "#teacherName ?: 'null' + '_' + " +
            "#className ?: 'null' + '_' + " +
            "#majorId ?: 'null' + '_' + " +
            "#courseId ?: 'null' + '_' + " +
            "#remaining ?: 'null' + '_' + " +
            "#pageNum + '_' + #pageSize")
    public Result getPage(String teacherName, String className,
                          Integer majorId, Integer courseId, Integer remaining,
                          Integer pageNum, Integer pageSize) {
        LambdaQueryWrapper<Clazz> clazzLambdaQueryWrapper = new LambdaQueryWrapper<>();
        if(teacherName != null && !teacherName.isEmpty()){
            List<User> user = userMapper.selectlike(teacherName);
            for (User u : user) {
                clazzLambdaQueryWrapper.eq(Clazz::getTeacherId, u.getUserId());
            }
        }
        if(className != null && !className.isEmpty()){
            clazzLambdaQueryWrapper.like(Clazz::getClassName, className);
        }
        if(majorId != null){
            clazzLambdaQueryWrapper.eq(Clazz::getMajorId, majorId);
        }
        if(courseId != null){
            clazzLambdaQueryWrapper.eq(Clazz::getCourseId, courseId);
        }
        if(remaining == 0){
            clazzLambdaQueryWrapper.eq(Clazz::getRemaining, 0);
        }
        List<Clazz> clazzList = classMapper.selectList(clazzLambdaQueryWrapper);

        List<ClassVO> classVOList = new ArrayList<>();
        for (Clazz clazz : clazzList) {
            if(clazz.getState() == 1){
                continue;
            }
            ClassVO classVO = new ClassVO();
            classVO.setId(clazz.getId());
            classVO.setTeacherId(clazz.getTeacherId());
            classVO.setClassName(clazz.getClassName());
            classVO.setMajorId(clazz.getMajorId());
            classVO.setCourseId(clazz.getCourseId());
            classVO.setNumber(clazz.getNumber());
            classVO.setExisting(clazz.getExisting());
            classVO.setRemaining(clazz.getRemaining());
            classVO.setVersion(clazz.getVersion());
            User teacher = userMapper.selectByteacherId(clazz.getTeacherId());
            classVO.setTeacherName(teacher != null ? teacher.getUserName() : "玉渊潭天");
            Major major = majorMapper.selectById(clazz.getMajorId());
            classVO.setMajorName(major != null ? major.getMajorName() : "未知专业");
            Course course = courseMapper.selectById(clazz.getCourseId());
            classVO.setCourseName(course != null ? course.getCourseName() : "未知课程");

            Schedule schedule = scheduleMapper.selectOne(new QueryWrapper<Schedule>().eq("class_id", clazz.getId()));
            if(schedule != null){
                classVO.setStartWeek(schedule.getStartWeek());
                classVO.setEndWeek(schedule.getEndWeek());
                classVO.setDay(schedule.getDay());
                classVO.setStartTime(schedule.getStartTime());
                classVO.setEndTime(schedule.getEndTime());
                classVO.setClassroom(schedule.getClassroom());
            }else{
                classVO.setClassroom("无上课地点");
            }
            classVOList.add(classVO);
        }
        Page<ClassVO> page = new Page<>();
        page.setRecords(classVOList);
        page.setTotal(classVOList.size());
        page.setCurrent(pageNum);
        return Result.success(page);
    }*/
}
